﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Web;

namespace FB.Data
{

    public interface IRepository<T> where T : class
    {
        IQueryable<T> Get(
            Expression<Func<T, bool>> filter = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            string includeProperties = ""
            );
        IQueryable<T> GetPagedList(
            Expression<Func<T, bool>> filter ,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy ,
            int pageIndex, int pageSize, out int totalRecords,
            string includeProperties = ""
            );
        IQueryable<T> GetPagedListByFilters(
            IList<Expression<Func<T, bool>>> filters,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
            int pageIndex, int pageSize, out int totalRecords,
            string includeProperties = ""
            );
        T GetByKey(params object[] key);
        T Insert(T entity);
        void Delete(params object[] id);
        void Delete(T entityToDelete);
        void Delete(Expression<Func<T, bool>> where);
        void Update(T entityToUpdate);
        IEnumerable<T> GetWithRawSql(string query, params object[] parameters);
        int SaveChanges();
        int SaveChanges(bool AutoRefresh);
    }


    public class Repository<TContext, TEntity> : IDisposable, IRepository<TEntity>
        where TEntity : class
        where TContext : FB.Data.Models.DbContextBase, new()
    {
        protected TContext _context;
        protected DbSet<TEntity> _dbSet;
        bool _disposed = false;

        //public Repository() : this(new TContext()) { }
        public Repository(TContext context)
        {
            this._context = context;
            this._dbSet = context.Set<TEntity>();
        }

        public System.Collections.ObjectModel.ObservableCollection<TEntity> Local
        {
            get { return _dbSet.Local; }
        }

        public virtual IQueryable<TEntity> Get()
        {
            return Get(null, null, "");
        }

        public virtual IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = _dbSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query);
            }
            else
            {
                return query;
            }
        }

        public virtual IQueryable<TEntity> GetEntities(
          IList<Expression<Func<TEntity, bool>>> filters=null ,
          Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
          string includeProperties = "")
        {
            IQueryable<TEntity> query = _dbSet;


            if (filters != null)
            {
                foreach (var item in filters)
                {
                    query = query.Where(item);
                }
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query);
            }
            else
            {
                return query;
            }
        }

        public virtual TEntity GetByKey(params object[] id)
        {
            return _dbSet.Find(id);
        }

        public virtual TEntity Insert(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Added;
            return _dbSet.Add(entity);
        }

        public virtual void Delete(params object[] id)
        {
            TEntity entityToDelete = _dbSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete(TEntity entityToDelete)
        {
            // for EF 6.0
            if (_context.Entry(entityToDelete).State == EntityState.Detached)
            {
                _dbSet.Attach(entityToDelete);
            }
            _dbSet.Remove(entityToDelete);
        }


        public virtual void Delete(Expression<Func<TEntity, bool>> filter)
        {
            var objects = _context.Set<TEntity>().Where(filter).AsEnumerable();
            foreach (var item in objects)
            {
                Delete(item);
            }
        }


        public virtual void Update(TEntity entityToUpdate)
        {
            _dbSet.Attach(entityToUpdate);
            _context.Entry(entityToUpdate).State = EntityState.Modified;
        }


        public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
        {
            return _dbSet.SqlQuery(query, parameters).ToList();
        }


        public virtual int SaveChanges()
        {
            return SaveChanges(false);
        }

        public virtual int SaveChanges(bool AutoRefresh)
        {
            var result = _context.SaveChanges();
            if (AutoRefresh)
                _context.Refresh();

            return result;
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                    _context = null;
                }
            }
            this._disposed = true;
        }

        public virtual IQueryable<TEntity> GetPagedList(
           Expression<Func<TEntity, bool>> filter,
           Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy,
               int pageIndex, int pageSize, out int totalRecords,
           string includeProperties = "")
        {
            totalRecords = 0;
            IQueryable<TEntity> query = _dbSet;
            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
            totalRecords = query.Count();

            return orderBy(query).Skip(pageIndex*pageSize).Take(pageSize) ;

        }
 

        public IQueryable<TEntity> GetPagedListByFilters(IList<Expression<Func<TEntity, bool>>> filters, Func<IQueryable<TEntity>,
         IOrderedQueryable<TEntity>> orderBy, int pageIndex, int pageSize, out int totalRecords, string includeProperties = "")
        {
            totalRecords = 0;
            IQueryable<TEntity> query = _dbSet;
            if (filters != null)
            {
                foreach (var item in filters)
                {
                    query = query.Where(item);
                }
            } 
            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
            totalRecords = query.Count(); 
            return orderBy(query).Skip(pageIndex * pageSize).Take(pageSize);
        }




 

     
    }
}

